import java.util.*;
import java.util.concurrent.*;

public class Swarm implements CallbackInterface{

	static final int numExp = 50;

	public static String coeToString(double[] coe){
		String r = "";
		for(int i = 0; i + 1 < Explorer.coes; i++){
			r += coe[i] + ", ";
		}
		r += coe[Explorer.coes - 1];
		return r;
	}

	private int curNum;
	private int count = 0;

	private int curbest, curworst;
	private double[] curbe, curwe;
	private	int cbi = 0, cwi = 0;

	private double[] sd = new double[Explorer.coes];
	private double[] sum = new double[Explorer.coes];
	private double[] sqsum = new double[Explorer.coes];

	private int sinceLastTest = 0;

	private PTest ptest = new PTest(this);


	Explorer[] exs = new Explorer[numExp];

	Random r = new Random();

	double[] goe = new double[Explorer.coes];
	boolean[] hasTested = {true};
	int[] gval = {0, 0};

	//a[i] = r.nextDouble() * x + y; 0 <= i < tot
	private void randArray(double[] a, int tot, int x, int y)
	{
		for (int i = 0; i < tot; i ++)
		{
			a[i] = r.nextDouble() * x + y;
		}
	}

	public Swarm(){
		for(int i = 0; i < numExp; i++){
			double[] coe = new double[Explorer.coes],
			     	 dir = new double[Explorer.coes];

			randArray(coe, Explorer.coes, 10, 0);
			randArray(dir, Explorer.coes, 20, -10);

			exs[i] = new Explorer(coe, dir, this, i);
			exs[i].hasTested = hasTested;
			exs[i].gval = gval;
			exs[i].goe = goe;
			exs[i].r = r;
		}

		// PTest = new PTest(this);
		nextIteration();
	}

	public synchronized void iAmDone(int result, final double[] coe, int ind, int spawn){
		curbest = Math.max(result, curbest);
		if(curbest == result){
			curbe = coe;
		}
		curworst = Math.min(result, curworst);
		if(curworst == result){
			curwe = coe;
		}
		for(int j= 0; j < Explorer.coes; j++){
			sum[j] += coe[j];
			sqsum[j] += coe[j] * coe[j];
		}
		if(result > gval[0]){
			for(int j = 0; j < Explorer.coes; j++){
				goe[j] = coe[j];
			}
			gval[0] = result;
            gval[1] = 0;
			hasTested[0] = false;
		}
		curNum--;
		if(curNum == 0){
			updateParticles();
		}
	}

	public void testDone(int result)
	{
		gval[0] = result;
		hasTested[0] = true;

		nextIteration();
	}

	public void updateParticles(){
		double sdsum = 0;
		boolean converge = true;
        for(int i = 0; i < numExp; i++){
            exs[i].adjustDir();
            exs[i].move();
        }
        gval[1]++;
		for(int j = 0; j < Explorer.coes; j++){
			sd[j] = sqsum[j] / numExp - Math.pow(sum[j] / numExp, 2);
			converge = converge && (sd[j] < 1);
			sdsum += sd[j];
		}

		System.out.println("Sd: " + coeToString(sd));
		System.out.println("Current best: " + (curbest) + ", Coe: " + coeToString(curbe));
		System.out.println("Current worst: " + (curworst) + ", Coe: " + coeToString(curwe));
		System.out.println("Best: " + (gval[0]) + ", Coe: " + coeToString(goe));
		count++;
		System.out.println("Iteration: " + count);
		System.out.println("=====================================================");

		if (converge && false)
		{
			System.out.println("Converged.");
			for (int i = 0; i < numExp; i ++)
			{
				double[] newD = exs[i].getDir();
				randArray(newD, Explorer.coes, 20, -10);
			}

			converge = false;
		}

		if(!hasTested[0] && (sinceLastTest > 10 || r.nextDouble() < 0.1 || gval[0] > 3000000))
		{
			ptest.run(gval[0], goe);
			sinceLastTest = 0;
		} else
		{
			if(sdsum < 0.1)return;
			nextIteration();
		}
	}

	public void nextIteration(){
		curbest = 0;
		curworst = 999999999;
		sinceLastTest++;
		curNum = numExp;
		for(int i = 0; i < Explorer.coes; i++){
			sum[i] = 0;
			sqsum[i] = 0;
		}
		ExecutorService executor = Executors.newFixedThreadPool(8);

		for (int i = 0; i < numExp; i ++) {
			executor.execute(exs[i]);
		}

		executor.shutdown();
	}

	public static void main(String[] argv){
		Swarm swarm = new Swarm();
	}

}
